---
// Import the necessary CSS for AG Grid
import "ag-grid-community/styles/ag-grid.css";
import "ag-grid-community/styles/ag-theme-quartz.css";
---

<style>
  .ag-theme-quartz {
    font-family: monospace;
    --ag-background-color: #f5f5f4;
    --ag-header-background-color: white;
    --ag-odd-row-background-color: white;
    --good-color: #b9f6ce;
    --bad-color: #fdc9c9;
  }

  .ag-theme-quartz-dark {
    font-family: monospace;
    --ag-background-color: #27272a;
    --ag-header-background-color: #3f3f46;
    --ag-odd-row-background-color: #3f3f46;
    --good-color: #15803d;
    --bad-color: #b91c1c;
  }

  .good {
    background-color: var(--good-color);
  }
  .bad {
    background-color: var(--bad-color);
  }

  @media (max-width: 640px) {
    #legend {
      display: none;
    }
  }
</style>

<form id="benchmarks" class="text-sm">
  <div class="flex flex-row">
    <div class="flex flex-col">
      <label for="regionSelector" class="text-base font-bold">Region</label>
      <label class="text-base font-bold">===============</label>
      <div id="regionSelector" class="mb-4">
        <div>
          <input
            type="radio"
            id="seaRegionSelector"
            name="selectedRegion"
            value="sea"
            checked
          />
          <label for="seaRegionSelector">US West (Seattle)</label>
        </div>
        <div>
          <input
            type="radio"
            id="iadRegionSelector"
            name="selectedRegion"
            value="iad"
          />
          <label for="iadRegionSelector">US East (Virginia)</label>
        </div>
        <div>
          <input
            type="radio"
            id="cdgRegionSelector"
            name="selectedRegion"
            value="cdg"
          />
          <label for="cdgRegionSelector">Europe (Paris)</label>
        </div>
      </div>
    </div>
    <div class="ml-8 flex flex-col">
      <label for="mediaSelector" class="text-base font-bold">Prompt Type</label>
      <label class="text-base font-bold">===============</label>
      <div id="mediaSelector" class="mb-4">
        <div>
          <input
            type="radio"
            id="textMediaSelector"
            name="selectedMedia"
            value="text"
            checked
          />
          <label for="textMediaSelector">Text</label>
        </div>
        <div>
          <input
            type="radio"
            id="toolsMediaSelector"
            name="selectedMedia"
            value="tools"
          />
          <label for="toolsMediaSelector">Function</label>
        </div>
        <div>
          <input
            type="radio"
            id="imageMediaSelector"
            name="selectedMedia"
            value="image"
          />
          <label for="imageMediaSelector">Image</label>
        </div>
        <div>
          <input
            type="radio"
            id="audioMediaSelector"
            name="selectedMedia"
            value="audio"
          />
          <label for="audioMediaSelector">Audio</label>
        </div>
      </div>
    </div>
    <div
      id="legend"
      class="mb-4 ml-auto mt-10 flex flex-col justify-end text-left"
    >
      <div><span class="font-bold">TTFT:</span> Time To First Token</div>
      <div><span class="font-bold">TPS: </span>Tokens Per Second</div>
      <div>
        <span class="font-bold">Total Time: </span>From request to final token
      </div>
    </div>
  </div>

  <div
    id="myGrid"
    class="ag-theme-quartz hidden w-full bg-stone-100 font-mono text-sm text-gray-950 dark:bg-stone-900 dark:text-white"
  >
  </div>

  <div
    id="myFooter"
    class="ag-theme-quartz mt-4 flex flex-row items-center justify-between text-sm"
  >
    <div>
      <p class="inline-flex">
        <div class="inline-flex items-center">
          <span
            class="good w-18 rounded-md px-2 py-1 text-center text-sm ring-1 ring-inset ring-gray-500/10"
            >Fastest</span
          >
        </div>
        <div class="inline-flex items-center">
          <span
            class="bad w-18 rounded-md px-2 py-1 text-center text-sm ring-1 ring-inset ring-gray-500/10"
            >Slowest</span
          >
        </div>
      </p>
    </div>
    <div id="lastUpdated" class="text-right font-mono italic"></div>
  </div>
</form>

<script>
  import { createGrid } from "ag-grid-community";
  import {
    BenchmarkRegions,
    gridOptionsBase,
    TTFTDefinition,
    TPSDefinition,
    TotalTimeDefinition,
  } from "@/utils/DataGridDefinitions.ts";
  import { fetchJsonFile, fetchLatestJsonFile } from "@/utils/FetchData.ts";

  let gridApi;
  const urlParams = new URLSearchParams(window.location.search);
  let selectedRegion = urlParams.get("r") ?? "sea";
  let selectedMedia = urlParams.get("m") ?? "text";
  const dateStr = urlParams.get("d");
  const date = dateStr ? new Date(dateStr) : undefined;
  const initialData = await fetchData(date);

  async function fetchData() {
    return date
      ? fetchJsonFile(selectedRegion, selectedMedia, date)
      : fetchLatestJsonFile(selectedRegion, selectedMedia);
  }

  function setUrlParam(key: string, value?: string) {
    const url = new URL(window.location.href);
    if (value) {
      url.searchParams.set(key, value);
    } else {
      url.searchParams.delete(key);
    }
    window.history.replaceState({}, "", url);
  }

  async function updateRegion(region: string) {
    selectedRegion = region;
    updateGrid(await fetchData());
    setUrlParam("r", region);
  }

  async function updateMedia(media: string) {
    selectedMedia = media;
    updateGrid(await fetchData());
    setUrlParam("m", media);
  }

  function updateGrid(data) {
    gridApi.setRowData(data.results);
  }

  // Returns the background color for the cell based on the value
  function colFunction(params) {
    const currentColumnID = params.column.colId;
    const myGrid = document.getElementById("myGrid");
    const style = getComputedStyle(myGrid!);
    const map = {
      ttft: TTFTDefinition,
      tps: TPSDefinition,
      total_time: TotalTimeDefinition,
    };
    const def = map[currentColumnID];
    if (def) {
      const isGood =
        def.worstPerformance > def.bestPerformance
          ? params.value < def.bestPerformance
          : params.value > def.bestPerformance;
      const isBad =
        def.worstPerformance > def.bestPerformance
          ? params.value > def.worstPerformance
          : params.value < def.worstPerformance;
      if (isGood) {
        return { backgroundColor: style.getPropertyValue("--good-color") };
      } else if (isBad) {
        return { backgroundColor: style.getPropertyValue("--bad-color") };
      }
    }
    return {};
  }

  function filterToText(column) {
    if (!column) {
      return undefined;
    }
    if (column.operator) {
      const operator = column.operator === "OR" ? "|" : "&";
      return column.conditions.reduce(
        (acc, condition) =>
          acc + (acc ? operator : "") + filterToText(condition),
        "",
      );
    }
    return column.filter;
  }

  function textToFilter(text) {
    if (!text) {
      return undefined;
    }
    const opChar = ["|", "&"].find((ch) => text.includes(ch));
    if (opChar) {
      const parts = text.split(opChar);
      const operator = opChar === "|" ? "OR" : "AND";
      return {
        filterType: "text",
        operator,
        conditions: parts.map(textToFilter),
      };
    }
    return { filterType: "text", type: "contains", filter: text };
  }

  function makeProviderUrl(provider: string) {
    return provider.includes(".")
      ? `https://${provider}`
      : `https://${provider}.com`;
  }

  async function onDOMContentLoaded() {
    const gridDiv = document.querySelector("#myGrid") as HTMLElement;
    const gridOptions = gridOptionsBase;
    const isMobile = window.matchMedia("(max-width: 640px)").matches;
    const providerFilter = urlParams.get("pf");
    const modelFilter = urlParams.get("mf");

    gridOptions.columnDefs[0].cellRenderer = function (params) {
      return `<a href="${makeProviderUrl(params.value)}" class="hover:text-orange-600" target="_blank">${params.value}</a>`;
    };
    // remove TTFT and TPS columns on mobile
    if (isMobile) {
      gridOptions.columnDefs.splice(2, 2);
    }
    // set the style proc for each column
    gridOptions.columnDefs.forEach((columnDef) => {
      columnDef.cellStyle = colFunction;
    });
    gridOptions.onFilterChanged = (x) => {
      const filterModel = gridApi.getFilterModel();
      const providerFilter = filterToText(filterModel.provider);
      const modelFilter = filterToText(filterModel.model);
      setUrlParam("pf", providerFilter);
      setUrlParam("mf", modelFilter);
    };

    // create the grid and set the filter if needed
    gridApi = createGrid(gridDiv, gridOptions);
    let filterModel = {
      provider: textToFilter(providerFilter),
      model: textToFilter(modelFilter),
    };
    gridApi.setFilterModel(filterModel);

    // set the selected region and media and populate the grid
    const selectedRegionRadio = document.querySelector(
      `input[name=selectedRegion][value=${selectedRegion}]`,
    ) as HTMLInputElement;
    selectedRegionRadio.checked = true;
    const selectedMediaRadio = document.querySelector(
      `input[name=selectedMedia][value=${selectedMedia}]`,
    ) as HTMLInputElement;
    selectedMediaRadio.checked = true;
    updateGrid(initialData);
    gridDiv.classList.remove("hidden");

    // Add text for our last updated date
    const ourDiv = document.getElementById("lastUpdated");
    const lastUpdate = new Date(initialData.time).toLocaleDateString();
    ourDiv!.innerText = `Last Update: ${lastUpdate}`;

    // Add event listeners to the radio buttons to filter the grid data
    document
      .getElementById("benchmarks")!
      .addEventListener("change", async function (event) {
        if (event.target.name === "selectedRegion") {
          await updateRegion(event.target.value);
        } else if (event.target.name === "selectedMedia") {
          await updateMedia(event.target.value);
        }
      });
  }

  // Init the grid when the DOM is ready, or now if it has already loaded.
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", onDOMContentLoaded);
  } else {
    onDOMContentLoaded();
  }

  // Resize columns when the window resizes (eg orientation change on mobile)
  window.addEventListener("resize", () => gridApi.sizeColumnsToFit());

  // Watch for dark mode changes and refresh the grid accordingly
  const observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      if (mutation.attributeName === "class") {
        gridApi.refreshCells({ force: true });
      }
    });
  });
  observer.observe(document.documentElement, { attributes: true });
</script>
